package com.zxjbyte.yiyi.framework.oss.storage.local;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.zxjbyte.yiyi.framework.oss.factory.IOssConfigFactory;
import com.zxjbyte.yiyi.framework.oss.rule.IOssRule;
import com.zxjbyte.yiyi.framework.oss.storage.AbstractOssTemplate;
import com.zxjbyte.yiyi.framework.oss.storage.FileMetadata;
import com.zxjbyte.yiyi.framework.oss.storage.OssPutResult;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.InputStream;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 本地存储器模板类（生产不推荐）
 *
 * @Author zhangxingjia
 * @Date 2023/5/9 18:00
 * @Version: 1.0
 */
@Slf4j
@RequiredArgsConstructor
public class LocalOssTemplate extends AbstractOssTemplate implements LocalOssOperations{

    private static final String NO_SPACE_CN = "设备上没有空间";
    private static final String NO_SPACE_EN = "No space left on device";
    private static final String DISK_QUOTA_CN = "超出磁盘限额";
    private static final String DISK_QUOTA_EN = "Disk quota exceeded";

    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    private final ReentrantReadWriteLock.ReadLock readLock = lock.readLock();
    private final ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock();

    private final IOssConfigFactory factory;
    private final IOssRule ossRule;
    private LocalOssConfig config;

    private void init(){
        LocalOssConfigWrapper wrapper = factory.getLocalOssConfig();
        config = wrapper.getConfig();
    }

    @Override
    public FileMetadata statFile(String path) {
        readLock.lock();
        try {
            String filePath = getFilePath(path);
            byte[] content = null;
            try {
                content = FileUtil.readBytes(filePath);
            } catch (Exception ignored) {
            }
            if (content == null) {
                return null;
            }
            FileMetadata metadata = new FileMetadata();
            metadata.setFileContent(content);
            metadata.setLength(content.length);
            metadata.setPutTime(FileUtil.lastModifiedTime(filePath));
            metadata.setName(path);
            metadata.setLink(formatGetFileUrl(config.getDomain(), path));
            metadata.setContentType(FileUtil.getMimeType(path));
            metadata.setHash(DigestUtil.sha256Hex(content));
            return metadata;
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public OssPutResult putFile(InputStream input, String path) {
        // 此方法读取完毕之后会关闭流
        byte[] bytes = IoUtil.readBytes(input);
        return putFile(bytes, path);
    }

    @Override
    public OssPutResult putFile(byte[] content, String fileName) {
        return put(content, fileName);
    }

    @Override
    @SneakyThrows
    public OssPutResult putFile(MultipartFile file, String fileName) {
        return putFile(file.getInputStream(), fileName);
    }

    @Override
    public OssPutResult putFile(MultipartFile file) {
        return putFile(file, file.getOriginalFilename());
    }

    private OssPutResult put(byte[] content, String originalName){
        writeLock.lock();
        try {
            String path = ossRule.fileNameByTenant(originalName);
            // 执行写入
            String filePath = getFilePath(path);
            try {
                FileUtil.writeBytes(content, filePath);
            } catch (IORuntimeException ioe) {
                String errMsg = ioe.getMessage();
                if (NO_SPACE_CN.equals(errMsg) || NO_SPACE_EN.equals(errMsg) || errMsg.contains(DISK_QUOTA_CN) || errMsg
                        .contains(DISK_QUOTA_EN)) {
                    log.warn("磁盘满，自杀退出");
                    System.exit(0);
                }
            }
            OssPutResult result = new OssPutResult();
            result.setOriginalName(originalName);
            result.setName(path);
            result.setDomain(config.getDomain());
            result.setLink(formatGetFileUrl(config.getDomain(), path));
            // 记录上传日志
            ossRecordAddPublish(result, content.length, "local");
            return result;
        } finally {
            writeLock.unlock();
        }
    }

    @Override
    public boolean deleteFile(String path) {
        readLock.lock();
        try {
            File file = FileUtil.file(getFilePath(path));
            boolean delResult = false;
            if (file != null && file.exists()) {
                delResult = FileUtil.del(file);
            }
            log.debug("LOCAL Storage Execution Delete Files, current path: {}, execution result: {}", path, delResult);
            // 存储文件被删除之后删除日志记录（逻辑删除）
            ossRecordDeletePublish(path);
            return delResult;
        } finally {
            readLock.unlock();
        }
    }

    private String getFilePath(String path) {
        String writeBasePath = config.getWriteBasePath();
        if (!writeBasePath.endsWith(File.separator)) {
            writeBasePath += File.separator;
        }
        return writeBasePath + path;
    }

}
